package goredis

import (
	"fmt"
	"github.com/go-redis/redis"
	config "go-micro-framework/go_service/core/cofig"
	"log"
	"reflect"
	"time"
)

// 工厂单节点和哨兵共用接口
type RedisProxyTypeFactory interface {
	RedisProxyType() RedisProxyType
	RedisClientRouter() interface{}
}

// 具体工厂类 单结点
type RedisClientSingleFactory struct {
	proxyType RedisProxyType
	options   *redis.Options
	client    *redis.Client
}

func NewRedisClientSingleFactory(config RedisClientConfig) RedisClientSingleFactory {
	redisOptionsProxy := RedisOptionsProxy{}
	redisOptionsProxy.setRedisOptionsProxy(config)
	redisClient := redis.NewClient(redisOptionsProxy.clientOptions)
	clientFactory := RedisClientSingleFactory{proxyType: config.ProxyType, options: redisOptionsProxy.clientOptions, client: redisClient}
	return clientFactory
}

// 具体工厂类  哨兵
type RedisClientSentinelFactory struct {
	proxyType RedisProxyType
	options   *redis.Options
	client    *redis.SentinelClient
}

func NewRedisClientSentinelFactory(config RedisClientConfig) RedisClientSentinelFactory {
	redisOptionsProxy := RedisOptionsProxy{}
	redisOptionsProxy.setRedisOptionsProxy(config)
	redisClient := redis.NewSentinelClient(redisOptionsProxy.sentinelOptions)
	clientFactory := RedisClientSentinelFactory{proxyType: config.ProxyType, options: redisOptionsProxy.sentinelOptions, client: redisClient}
	return clientFactory
}

// 具体工厂类  哨兵模式下有主从之分
type RedisClientFailoverFactory struct {
	proxyType RedisProxyType
	options   *redis.FailoverOptions
	client    *redis.Client
}

func NewRedisClientFailoverFactory(config RedisClientConfig) RedisClientFailoverFactory {
	redisOptionsProxy := RedisOptionsProxy{}
	redisOptionsProxy.setRedisOptionsProxy(config)
	redisClient := redis.NewFailoverClient(redisOptionsProxy.failoverOptions)
	clientFactory := RedisClientFailoverFactory{proxyType: config.ProxyType, options: redisOptionsProxy.failoverOptions, client: redisClient}
	return clientFactory
}

// 具体工厂类 集群
type RedisClientClusterFactory struct {
	proxyType RedisProxyType
	options   *redis.ClusterOptions
	client    *redis.ClusterClient
}

func NewRedisClientClusterFactory(config RedisClientConfig) RedisClientClusterFactory {
	redisOptionsProxy := RedisOptionsProxy{}
	redisOptionsProxy.setRedisOptionsProxy(config)
	redisClient := redis.NewClusterClient(redisOptionsProxy.clusterOptions)
	clientFactory := RedisClientClusterFactory{proxyType: config.ProxyType, options: redisOptionsProxy.clusterOptions, client: redisClient}
	return clientFactory
}

/** 实现单结点redis的连接 **/
func (clientFactory *RedisClientSingleFactory) RedisClientRouter() interface{} {
	return clientFactory.client
}

/** 实现哨后结点redis的连接  创建 Redis Sentinel 客户端 **/
func (clientFactory *RedisClientSentinelFactory) RedisClientRouter() interface{} {
	return clientFactory.client
}

/**  创建 Redis Failover 客户端 **/
func (clientFactory *RedisClientFailoverFactory) RedisClientRouter() interface{} {
	return clientFactory.client
}

/**  创建 Redis Cluster 客户端 **/
func (clientFactory *RedisClientClusterFactory) RedisClientRouter() interface{} {
	return clientFactory.client
}

/** 实现单结点redis的连接 **/
func (clientFactory *RedisClientSingleFactory) RedisClient() *redis.Client {
	return clientFactory.client
}

/** 实现哨后结点redis的连接  创建 Redis Sentinel 客户端 **/
func (clientFactory *RedisClientSentinelFactory) RedisClient() *redis.SentinelClient {
	return clientFactory.client
}

/**  创建 Redis Failover 客户端 **/
func (clientFactory *RedisClientFailoverFactory) RedisClient() *redis.Client {
	return clientFactory.client
}

/**  创建 Redis Cluster 客户端 **/
func (clientFactory *RedisClientClusterFactory) RedisClient() *redis.ClusterClient {
	return clientFactory.client
}

/**  实现单结点redis的连接 **/
func (clientFactory *RedisClientSingleFactory) RedisProxyType() RedisProxyType {
	return RedisClientTypeSingle
}

/**  实现哨后结点redis的连接  创建 Redis Sentinel 客户端 **/
func (clientFactory *RedisClientSentinelFactory) RedisProxyType() RedisProxyType {
	return RedisClientTypeSentinel
}

/**  创建 Redis Failover 客户端 **/
func (clientFactory *RedisClientFailoverFactory) RedisProxyType() RedisProxyType {
	return RedisClientTypeFailover
}

/**  创建 Redis Cluster 客户端 **/
func (clientFactory *RedisClientClusterFactory) RedisProxyType() RedisProxyType {
	return RedisClientTypeCluster
}

type RedisProxy struct {
	proxyFactory RedisProxyTypeFactory
}

func NewRedisProxy(proxyTypeFactory RedisProxyTypeFactory) RedisProxy {
	proxy := RedisProxy{}
	proxy.proxyFactory = proxyTypeFactory
	return proxy
}
func (proxy *RedisProxy) RedisProxyType() RedisProxyType {
	return proxy.proxyFactory.RedisProxyType()
}

func (proxy *RedisProxy) IsProxyCluster() bool {
	return RedisClientTypeCluster == proxy.proxyFactory.RedisProxyType()
}

type RedisProxyType string

const (
	RedisClientTypeSingle   RedisProxyType = "single"
	RedisClientTypeSentinel RedisProxyType = "sentinel"
	RedisClientTypeFailover RedisProxyType = "failover"
	RedisClientTypeCluster  RedisProxyType = "cluster"
)

// redis 单结点配置代理工厂,哨兵配置代理工厂,聚群配置代码工厂
type RedisOptionsProxy struct {
	proxyType       RedisProxyType
	clientOptions   *redis.Options
	sentinelOptions *redis.Options
	failoverOptions *redis.FailoverOptions
	clusterOptions  *redis.ClusterOptions
}

/** 连接池容量及闲置连接数量
** go-redis包自带了连接池，会自动维护redis连接，因此创建一次client即可，不要查询一次redis就关闭client。
** 定义 Redis 客户端配置结构体
**/
type RedisClientConfig struct {
	ProxyType    RedisProxyType
	Addrs        []string
	MasterName   string
	Password     string        ""
	DB           int           "default:0"
	PoolSize     int           "default:15"              // 连接池最大socket连接数，默认为4倍CPU数， 4 * runtime.NumCPU
	MinIdleConns int           "default:10"              //在启动阶段创建指定数量的Idle连接，并长期维持idle状态的连接数不少于指定数量；。
	IdleTimeout  time.Duration "default:5 * time.Minute" //闲置超时，默认5分钟，-1表示取消闲置超时检查
	DialTimeout  time.Duration "default:5 * time.Second" //连接建立超时时间，默认5秒。
	ReadTimeout  time.Duration "default:3 * time.Second" //读超时，默认3秒， -1表示取消读超时
	WriteTimeout time.Duration "default:3 * time.Second" //写超时，默认等于读超时
	PoolTimeout  time.Duration "default:4 * time.Second" //当所有连接都处在繁忙状态时，客户端等待可用连接的最大等待时长，默认为读超时+1秒。
	//闲置连接检查包括IdleTimeout，MaxConnAge
	IdleCheckFrequency time.Duration "default: 60 * time.Second" //闲置连接检查的周期，默认为1分钟，-1表示不做周期性检查，只在客户端获取连接时对闲置连接进行处理。
	MaxConnAge         time.Duration "default:0 * time.Second"   //连接存活时长，从创建开始计时，超过指定时长则关闭连接，默认为0，即不关闭存活时长较长的连接
	//命令执行失败时的重试策略
	MaxRetries      time.Duration "default:0"                      // 命令执行失败时，最多重试多少次，默认为0即不重试
	MinRetryBackoff time.Duration "default:8 * time.Millisecond"   //每次计算重试间隔时间的下限，默认8毫秒，-1表示取消间隔
	MaxRetryBackoff time.Duration "default:512 * time.Millisecond" //每次计算重试间隔时间的上限，默认512毫秒，-1表示取消间隔
}

// 通过代理名称,为单结点配置代理工厂,哨兵配置代理工厂,聚群配置代码工厂 付值;
func (redisOptionsProxy *RedisOptionsProxy) setRedisOptionsProxy(config RedisClientConfig) {
	redisOptionsProxy.proxyType = config.ProxyType
	switch config.ProxyType {
	case RedisClientTypeSingle:
		redisOptionsProxy.clientOptions = &redis.Options{
			Addr:         config.Addrs[0],
			Password:     config.Password, // no password set
			DB:           config.DB,       // use default DB
			PoolSize:     config.PoolSize,
			MinIdleConns: config.MinIdleConns,
			IdleTimeout:  config.IdleTimeout,
		}
		break
	case RedisClientTypeSentinel:
		redisOptionsProxy.sentinelOptions = &redis.Options{
			Addr:         config.Addrs[0],
			Password:     config.Password, // no password set
			DB:           config.DB,       // use default DB
			PoolSize:     config.PoolSize,
			MinIdleConns: config.MinIdleConns,
			IdleTimeout:  config.IdleTimeout,
		}
		break
	case RedisClientTypeFailover:
		redisOptionsProxy.failoverOptions = &redis.FailoverOptions{
			Password:      config.Password, // no password set
			DB:            config.DB,       // use default DB
			PoolSize:      config.PoolSize,
			MinIdleConns:  config.MinIdleConns,
			IdleTimeout:   config.IdleTimeout,
			MasterName:    config.MasterName,
			SentinelAddrs: config.Addrs,
			//SentinelAddrs: []string{
			//	"192.168.2.201:8000",
			//	"192.168.2.201:8001",
			//	"192.168.2.201:8002",
			//},
		}
		break
	case RedisClientTypeCluster:
		redisOptionsProxy.clusterOptions = &redis.ClusterOptions{
			Password:     config.Password, // no password set
			PoolSize:     config.PoolSize,
			MinIdleConns: config.MinIdleConns,
			IdleTimeout:  config.IdleTimeout,
			Addrs:        config.Addrs,
			//Addrs: []string{
			//	"192.168.2.201:7000",
			//	"192.168.2.201:7001",
			//	"192.168.2.201:7002",
			//},

		}
	}
}

///** redis 单结点代理工厂,哨兵代理工厂,聚群代码工厂  **/
//type RedisProxy struct {
//	proxyType RedisProxyType
//	//proxyFactory   ConRedisClientSingleFactory
//	singleFactory   *ConRedisClientSingleFactory
//	sentinelFactory *ConRedisClientSentinelFactory
//	failoverFactory *ConRedisClientFailoverFactory
//	clusterFactory  *ConRedisClientClusterFactory
//}

func NewRedisProxyRouterDefault(proxyType RedisProxyType) *RedisProxy {
	//创建两个封装对象
	single := RedisClientConfig{
		ProxyType: RedisClientTypeSingle,
		Addrs:     []string{"192.168.2.201:6379"},
		Password:  "123456", // no password set 123456
		DB:        0,
	}
	failover := RedisClientConfig{
		ProxyType: RedisClientTypeFailover,
		Addrs:     []string{"192.168.2.201:7001", "192.168.2.201:7002", "192.168.2.201:7003"},
		Password:  "", // no password set 123456

	}
	cluster := RedisClientConfig{
		ProxyType: RedisClientTypeCluster,
		Addrs:     []string{"192.168.2.201:7001", "192.168.2.201:7002", "192.168.2.201:7003"},
		Password:  "", // no password set 123456

	}
	var proxy RedisProxy
	switch proxyType {
	case RedisClientTypeSingle:
		client := NewRedisClientSingleFactory(single)
		proxy = NewRedisProxy(&client)
		//proxy. = &client
		break
	case RedisClientTypeSentinel:
		client := NewRedisClientSentinelFactory(single)
		proxy = NewRedisProxy(&client)
		//proxy.sentinelFactory = &client
		break
	case RedisClientTypeFailover:
		client := NewRedisClientFailoverFactory(failover)
		proxy = NewRedisProxy(&client)
		//proxy.failoverFactory = &client
		break
	case RedisClientTypeCluster:
		client := NewRedisClientClusterFactory(cluster)
		proxy = NewRedisProxy(&client)

		//proxy.clusterFactory = &client
	default:
		client := NewRedisClientSingleFactory(single)
		proxy = NewRedisProxy(&client)
		log.Println("this is a warning log message GetRedisProxyRouter[{}] is null ", proxy)
	}

	return &proxy
}
func NewRedisProxyRouter(redisConfig config.RedisConfig) *RedisProxy {
	//创建两个封装对象

	var proxy RedisProxy
	var proxyType = RedisProxyType(redisConfig.ProxyType)
	switch proxyType {
	case RedisClientTypeSingle:
		single := RedisClientConfig{
			ProxyType: RedisClientTypeSingle,
			Addrs:     redisConfig.Addrs,
			Password:  redisConfig.Password,
			DB:        redisConfig.DB,
		}
		client := NewRedisClientSingleFactory(single)
		proxy = NewRedisProxy(&client)
		//proxy. = &client
		break
	case RedisClientTypeSentinel:
		single := RedisClientConfig{
			ProxyType: RedisClientTypeSingle,
			Addrs:     redisConfig.Addrs,
			Password:  redisConfig.Password,
			DB:        redisConfig.DB,
		}
		client := NewRedisClientSentinelFactory(single)
		proxy = NewRedisProxy(&client)
		//proxy.sentinelFactory = &client
		break
	case RedisClientTypeFailover:
		failover := RedisClientConfig{
			ProxyType: RedisClientTypeFailover,
			Addrs:     redisConfig.Addrs,
			Password:  redisConfig.Password,
		}
		client := NewRedisClientFailoverFactory(failover)
		proxy = NewRedisProxy(&client)
		//proxy.failoverFactory = &client
		break
	case RedisClientTypeCluster:
		cluster := RedisClientConfig{
			ProxyType: RedisClientTypeCluster,
			Addrs:     redisConfig.Addrs,
			Password:  redisConfig.Password,
		}
		client := NewRedisClientClusterFactory(cluster)
		proxy = NewRedisProxy(&client)
		//proxy.clusterFactory = &client
	default:
		single := RedisClientConfig{
			ProxyType: RedisClientTypeSingle,
			Addrs:     redisConfig.Addrs,
			Password:  redisConfig.Password,
			DB:        redisConfig.DB,
		}
		client := NewRedisClientSingleFactory(single)
		proxy = NewRedisProxy(&client)
		log.Println("this is a warning log message GetRedisProxyRouter[{}] is null ", proxy)
	}
	return &proxy
}

//func (proxy RedisProxy) GetRedisProxyRouter(redisProxyType RedisProxyType) {
//	//创建两个封装对象
//
//	switch redisProxyType {
//	case RedisClientTypeSingle:
//		proxy.singleFactory.RedisClientRouter()
//		break
//	case RedisClientTypeFailover:
//		proxy.sentinelFactory.RedisClientRouter()
//	case RedisClientTypeSentinel:
//		proxy.sentinelFactory.RedisClientRouter()
//		break
//	case RedisClientTypeCluster:
//		proxy.clusterFactory.RedisClientRouter()
//		break
//	default:
//		log.Println("this is a warning log message GetRedisProxyRouter[{}] is null ", proxy)
//	}
//}

// 定义代理类
type RedisClientProxy struct {
	target reflect.Value
}

func (proxy *RedisProxy) argsValue(args ...[]string) []reflect.Value {
	var values []reflect.Value
	for _, arg := range args {
		values = append(values, reflect.ValueOf(arg))
	}
	return values
}

func (proxy *RedisProxy) ValueOf(args ...interface{}) []reflect.Value {
	var values []reflect.Value
	for _, arg := range args {
		if strs, ok := arg.([]string); ok {
			for _, str := range strs {
				values = append(values, reflect.ValueOf(str))
			}
		} else {
			values = append(values, reflect.ValueOf(arg))
		}

	}
	return values
}

/** 通过策略模式获取对应的操作对象 **/
func (proxy RedisProxy) Router(methodByName string, args ...interface{}) interface{} {
	var values = proxy.ValueOf(args...)
	/** 通过策略模式获取对应的操作对象 **/
	router := reflect.ValueOf(proxy.proxyFactory.RedisClientRouter())
	callVale := router.MethodByName(methodByName).Call(values)
	iValue := callVale[0].Interface()
	//cmd := iValue.(*redis.StringCmd)
	return iValue
}

func (proxy RedisProxy) RouterPipeline(methodByName string, keys ...string) []redis.Cmder {
	var _proxy = proxy.proxyFactory.RedisClientRouter()
	client, ok := _proxy.(*redis.ClusterClient)
	if ok {
		pipeline := client.Pipeline()
		for _, key := range keys {
			/** 通过策略模式获取对应的操作对象 **/
			args := []reflect.Value{reflect.ValueOf(key)}
			pipe := reflect.ValueOf(pipeline)
			pipe.MethodByName(methodByName).Call(args)
		}
		result, err := pipeline.Exec()
		//fmt.Println("DelPipeline   result >>", result)
		if err != nil {
			return nil
		}
		return result
	}
	return nil
}

func (proxy *RedisProxy) routerSwitch(methodByName string, args ...interface{}) interface{} {
	var values = proxy.ValueOf(args...)
	//
	switch proxy.RedisProxyType() {
	case RedisClientTypeSingle:
		router := reflect.ValueOf(proxy.proxyFactory.RedisClientRouter())
		callVale := router.MethodByName(methodByName).Call(values)
		iValue := callVale[0].Interface()
		//cmd := iValue.(*redis.StringCmd)
		return iValue
	case RedisClientTypeFailover:
		router := reflect.ValueOf(proxy.proxyFactory.RedisClientRouter())
		callVale := router.MethodByName(methodByName).Call(values)
		iValue := callVale[0].Interface()
		return iValue
	case RedisClientTypeSentinel:
		router := reflect.ValueOf(proxy.proxyFactory.RedisClientRouter())
		callVale := router.MethodByName(methodByName).Call(values)
		iValue := callVale[0].Interface()
		return iValue
		//return cmd
	case RedisClientTypeCluster:
		router := reflect.ValueOf(proxy.proxyFactory.RedisClientRouter())
		callVale := router.MethodByName(methodByName).Call(values)
		iValue := callVale[0].Interface()
		return iValue
		//return cmd
	default:
		log.Println("this is a warning log message GetRedisProxyRouter[{}] is null ", proxy)
	}

	return nil
}

/* 实现 reflect.Value 接口中的 Call 方法  **/
func (proxy RedisProxy) Call(methodByName string, args []reflect.Value) []reflect.Value {
	fmt.Println("Before calling MyMethod...")

	// 通过反射调用被代理对象的方法
	result := proxy.Call(methodByName, args)

	fmt.Println("After calling MyMethod...")

	return result
}
